package com.clp.protocol.iec104.apdu.asdu;

import com.clp.protocol.core.common.frame.InitializableFrameClip;
import com.clp.protocol.core.utils.ByteUtil;
import com.clp.protocol.iec104.apdu.asdu.info_obj.CInfoObj;
import com.clp.protocol.iec104.apdu.asdu.info_obj.InfoObj;
import com.clp.protocol.iec104.apdu.asdu.info_obj.NcInfoObj;
import com.clp.protocol.iec104.definition.ConstVal;
import com.clp.protocol.iec104.definition.TypeTag;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class IAsdu implements InitializableFrameClip<IAsdu> {
    /**
     * 类型标识，1字节
     */
    private TypeTag typeTag;

    /**
     * 可变结构限定词，
     */
    private Vsq vsq;

    /**
     * 传输原因，
     */
    private Cot cot;

    /**
     * 终端地址，2个字节，也就是应用服务数据单元公共地址
     */
    private int rtuAddr;

    /**
     * 信息体列表
     */
    private List<InfoObj> infoObjs;

    @Override
    public IAsdu initBy(ByteBuf buf) {
        // 1、获取类型标识
        this.typeTag = TypeTag.gain(buf.readByte());
        // 2、获取可变结构限定词
        this.vsq = new Vsq().initBy(buf);
        // 3、获取传输原因
        this.cot = new Cot().initBy(buf);
        // 4、获取应用服务单元公共地址（RTU地址）
        this.rtuAddr = buf.readShortLE() & 0xFFFF;
        // 5、获取信息对象列表
        this.infoObjs = new ArrayList<>();
        if (!typeTag.hasVsq()) {
            // 可变结构限定词无效的情况：文件传输
        } else {
            switch (vsq.getSq()) {
                case CONTINUE:
                    // 连续的，前三个字节是基地址
                    byte[] infoElemBytes = new byte[ConstVal.INFO_ELEM_ADDRESS_LEN];
                    buf.readBytes(infoElemBytes);
                    int addr = ByteUtil.bytes3ToIntLE(infoElemBytes);

                    // 设置信息元素列表（至少有循环一次）
                    int num = 0;
                    while (num < vsq.getNum()) {
                        infoObjs.add(new CInfoObj(addr) {
                            @Override
                            public TypeTag typeTag() {
                                return typeTag;
                            }
                        }.initBy(buf));
                        addr += 1;
                        num += 1;
                    }
                    break;
                case NOT_CONTINUE:
                    // 设置信息元素列表（至少有循环一次）
                    num = 0;
                    while (num < vsq.getNum()) {
                        // 设置信息元素列表
                        infoObjs.add(new NcInfoObj() {
                            @Override
                            public TypeTag typeTag() {
                                return typeTag;
                            }
                        }.initBy(buf));
                        num += 1;
                    }
                    break;
            }
        }
        return this;
    }

    @Override
    public boolean isValid() {
        if (typeTag == null) return false;
        if (vsq == null || !vsq.isValid()) return false;
        if (cot == null || !cot.isValid()) return false;
        if (rtuAddr < 0 || rtuAddr > 0xFFFF) return false;
        if (infoObjs == null || infoObjs.isEmpty()) return false;
        for (InfoObj infoObj : infoObjs) {
            if (!infoObj.isValid()) return false;
        }
        return true;
    }

    @Override
    public void writeTo(ByteBuf buf) {
        buf.writeByte(typeTag.getVal());
        vsq.writeTo(buf);
        cot.writeTo(buf);
        buf.writeShortLE(rtuAddr);
        if (!typeTag.hasVsq()) {
            // 可变结构限定词无效的情况：文件传输
            infoObjs.get(0).writeTo(buf);
        } else {
            switch (vsq.getSq()) {
                case CONTINUE:
                    // 连续的，取第1个信息体的地址
                    buf.writeBytes(ByteUtil.intToBytes3LE(infoObjs.get(0).getAddr()));
                    int num = 0;
                    while (num < vsq.getNum()) {
                        infoObjs.get(num).writeTo(buf);
                        num += 1;
                    }
                    break;
                case NOT_CONTINUE:
                    // 设置信息元素列表（至少有循环一次）
                    num = 0;
                    while (num < vsq.getNum()) {
                        infoObjs.get(num).writeTo(buf);
                        num += 1;
                    }
                    break;
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("     [ 类型标识：").append(typeTag.getDesc()).append("(").append(typeTag.getVal() & 0xFF).append(")");
        sb.append(", 可变结构限定词：").append(vsq);
        sb.append(", 传输原因：").append(cot);
        sb.append(", 公共地址：").append(rtuAddr);
        sb.append(" ]\n");
        if (!infoObjs.isEmpty()) {
            sb.append("       信息元素集：");
            for (int i = 0; i < infoObjs.size(); i++) {
                sb.append("\n     [ 信息体").append(i + 1).append("：").append(infoObjs.get(i));
                sb.append(" ]");
            }
        }
        sb.append("\n");
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        IAsdu iAsdu = (IAsdu) o;
        return rtuAddr == iAsdu.rtuAddr && typeTag == iAsdu.typeTag && Objects.equals(vsq, iAsdu.vsq) && Objects.equals(cot, iAsdu.cot) && Objects.equals(infoObjs, iAsdu.infoObjs);
    }

    @Override
    public int hashCode() {
        return Objects.hash(typeTag, vsq, cot, rtuAddr, infoObjs);
    }
}
